#pragma once
#include <mqcomm/Message.pb.h>
#include <mqcomm/Helper.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
using namespace std;

namespace rbmq {
    class Router {
    public :
        // 判断routing_key是否合法
        static bool routingKeyIsLegal(const string& routing_key) {
            // routing_key只需要判断是否包含非法字符
            // 合法字符：a~z A~Z 0~9 . _
            unordered_set<char> _hash;
            for (char ch = 'a'; ch <= 'z'; ch++) {
                _hash.insert(ch);
            }
            for (char ch = 'A'; ch <= 'Z'; ch++) {
                _hash.insert(ch);
            }
            for (char ch = '0'; ch <= '9'; ch++) {
                _hash.insert(ch);
            }
            _hash.insert('.');
            _hash.insert('_');
            for (auto ch : routing_key) {
                if (!_hash.count(ch)) {
                    return false;
                }
            }
            return true;
        }

        // 判断binding_key是否合法
        static bool bindingKeyIsLegal(const string& binding_key) {
            // binding_key的合法规则：
            // 1、判断是否包含有菲菲字符：(合法的字符有: a~z A~Z 0~9 . _ * # )
            unordered_set<char> _hash;
            for (char ch = 'a'; ch <= 'z'; ch++) {
                _hash.insert(ch);
            }
            for (char ch = 'A'; ch <= 'Z'; ch++) {
                _hash.insert(ch);
            }
            for (char ch = '0'; ch <= '9'; ch++) {
                _hash.insert(ch);
            }
            _hash.insert('.');
            _hash.insert('_');
            _hash.insert('*');
            _hash.insert('#');
            for (auto ch : binding_key) {
                if (!_hash.count(ch)) {
                    return false;
                }
            }
            // 2、* 和 # 必须独立存在，不能和其他的字母组成单词
            vector<string> tokens;
            StringHelper::splitString(binding_key, &tokens, ".");
            for (auto& str : tokens) {
                if (str.size() > 1 && (str.find("*") != string::npos || str.find("#") != string::npos)) {
                    return false;
                }
            }
            // 3、* 和 # 不能连续出现(* 和 * 是可以连续出现的)
            for (int i = 1; i < tokens.size(); i++) {
                if (tokens[i] == "*" && tokens[i - 1] == "#") {
                    return false;
                }
                if (tokens[i] == "#" && tokens[i - 1] == "*") {
                    return false;
                }
                if (tokens[i] == "#" && tokens[i - 1] == "#") {
                    return false;
                }                                
            }
            return true;

        }

        // 路由匹配
        static bool route(mqNs::ExchangeType type, const string& binding_key, const string& routing_key) {
            // 如果交换机类型是直接交换，直接返回routing_key和binding_key是否相等
            if (type == mqNs::ExchangeType::DIRECT) {
                return routing_key == binding_key;
            }
            // 如果交换机类型是广播交换，直接返回true
            if (type == mqNs::ExchangeType::FANOUT) {
                return true;
            }
            // 如果交换机类型是主题交换，就要按陪陪规则进行模式匹配
            vector<string> p;
            StringHelper::splitString(binding_key, &p, ".");
            vector<string> s;
            StringHelper::splitString(routing_key, &s, ".");
            int n = s.size(), m = p.size();
            vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
            // 初始化
            dp[0][0] = true;
            for (int i = 0; i < m; i++) {
                if (p[i] == "#") {
                    dp[0][i + 1] = true;
                } else {
                    break;
                }
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= m; j++) {
                    if (p[j - 1] == "#") {
                        dp[i][j] = dp[i - 1][j] || dp[i][j - 1];  
                    } else if (p[j - 1] == "*") {
                        dp[i][j] = dp[i - 1][j - 1];
                    } else {
                        dp[i][j] = s[i - 1] == p[j - 1] && dp[i - 1][j - 1];
                    }
                }
            }
            return dp[n][m];
        }

    };

};